using System;
using System.Collections.Generic;
using System.Text;
using MacomberMap.Common.Types;
using System.Data.SQLite;
using MacomberMap.Common.Database_Interface.CIM;
using MacomberMap.Common.Database_Interface.CIM.Components;
using System.Xml;
using System.Windows.Forms;
using System.ComponentModel;
using System.Data.Common;
using System.Data;
using System.Drawing;
using System.IO;

namespace MacomberMap.Loader.Export.NOMCRs
{
    /// <summary>
    /// This class holds information on an OTS rule
    /// </summary>
    [MM_Serializable_Target( MM_Serializable_Target.enumTargetType.TypeCollection)]
    public class MM_OTS_Rule : MM_Serializable, IComparable<MM_OTS_Rule>
    {
        #region Variable declarations
        private string _Constraint_Value, _ComparisonParameter, _ElementType, _Name;
        private enumRuleType _RuleType;
        private Dictionary<String, String> _Attributes = new Dictionary<String, String>(10);
        private Dictionary<String, CIM_Element> UniqueNames = null;
        
        /// <summary>
        /// The unique identifier of the rule
        /// </summary>
        [Category("Definition"), Description("The unique identifier of the rule"), MM_Serializable_Target(MM_Serializable_Target.enumTargetType.Index)]
        public int UID=-1;

        /// <summary>The base node associated with the OTS rule</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.None)]
        public TreeNode BaseNode;

        /// <summary>The name of the rule</summary>
        [Category("Definition"), Description("The name of the rule"), MM_Serializable_Target( MM_Serializable_Target.enumTargetType.TypeCollection)]
        public String Name
        {
            get { return _Name; }
            set { _Name = value; }
        }

        /// <summary>The types of rules available</summary>
        public enum enumRuleType
        {
            /// <summary>Elements of type A must be connected to elements of type B</summary>
            A_MustHave_B,

            ///<summary>Element of type A must have attribute B limited to C</summary>
            AttributeLimit,

            /// <summary>If a substation has one or more of element type A, they must have B</summary>
            Substation_Must_Have,

            /// <summary>Across all elements of type A, attribute B must be unique.</summary>
            RequireUnique
        }

        /// <summary>The rule type</summary>
        [Category("Definition"), Description("The rule type"), MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public enumRuleType RuleType
        {
            get { return _RuleType; }
            set { _RuleType = value; }
        }

        /// <summary>The element type / first component</summary>
        [Category("Configuration"), Description("The element type"), MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public String ElementType
        {
            get { return _ElementType; }
            set { _ElementType = value; }
        }

        /// <summary>
        /// The attributes to be used for new variables, when created
        /// </summary>
        [Category("Configuration"), Description("The attributes to be used for new variables, when created (name=value)"), MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public Dictionary<String,String> Attributes
        {
            get { return _Attributes; }
            set { _Attributes = value; }
        }


        /// <summary>The second attribute, whether element type, attribute type, or numeric value</summary>
        [Category("Configuration"), Description("The second attribute, whether element type, attribute type, or numeric value"), MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public String ComparisonParameter
        {
            get { return _ComparisonParameter; }
            set { _ComparisonParameter = value; }
        }


        /// <summary>The constraint on the item</summary>
        [Category("Configuration"), Description("The constraint on the item"), MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public String Constraint_Value
        {
            get { return _Constraint_Value; }
            set { _Constraint_Value = value; }
        }

        /// <summary>The ListView Item associated with this rule</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.None)]
        public ListViewItem lvI;
        #endregion

        #region Initialization
        /// <summary>
        /// Initialize a new OTS rule
        /// </summary>
        /// <param name="BaseElement"></param>
        /// <param name="lvRules"></param>
        public MM_OTS_Rule(XmlElement BaseElement, ListView lvRules)
            : base(BaseElement)
        {
            lvI = lvRules.Items.Add(this.Name);
            lvI.SubItems.Add(this.RuleType.ToString());
            lvI.SubItems.Add(ElementType);
            lvI.SubItems.Add(ComparisonParameter);
            lvI.SubItems.Add(Constraint_Value);
            lvI.Tag = this;
        }

        /// <summary>
        /// Initialize a new OTS rule
        /// </summary>
        /// <param name="DataReader"></param>
        /// <param name="lvRules"></param>
        public MM_OTS_Rule(DbDataReader DataReader, ListView lvRules)
            : base(DataReader)
        {
            lvI = lvRules.Items.Add(this.Name);
            lvI.SubItems.Add(this.RuleType.ToString());
            lvI.SubItems.Add(ElementType);
            lvI.SubItems.Add(ComparisonParameter);
            lvI.SubItems.Add(Constraint_Value);
            lvI.Tag = this;
        }

        /// <summary>
        /// Initialize a new OTS rule
        /// </summary>
        /// <param name="Name"></param>
        /// <param name="lvRules"></param>
        public MM_OTS_Rule(String Name, ListView lvRules)
        {
            lvI = lvRules.Items.Add(this.Name = Name);
            lvI.SubItems.Add(this.RuleType.ToString());
            lvI.SubItems.Add(ElementType);
            lvI.SubItems.Add(ComparisonParameter);
            lvI.SubItems.Add(Constraint_Value);
            lvI.Tag = this;
        }

        /// <summary>
        /// Initialize a new OTS rule
        /// </summary>
        /// <param name="BaseRule"></param>
        /// <param name="lvRules"></param>
        public MM_OTS_Rule(MM_OTS_Rule BaseRule, ListView lvRules)
        {
            lvI = lvRules.Items.Add(this.Name = "Copy of " + BaseRule.Name);
            lvI.SubItems.Add((this.RuleType = BaseRule.RuleType).ToString());
            lvI.SubItems.Add(this.ElementType = BaseRule.ElementType);
            lvI.SubItems.Add(this.ComparisonParameter = BaseRule.ComparisonParameter);
            lvI.SubItems.Add(this.Constraint_Value = BaseRule.Constraint_Value);
            lvI.Tag = this;
        }

        /// <summary>
        /// Refresh the list view item
        /// </summary>
        public void UpdateValues()
        {
            lvI.SubItems[0].Text = Name;
            lvI.SubItems[1].Text = RuleType.ToString();
            lvI.SubItems[2].Text = ElementType;
            lvI.SubItems[3].Text = ComparisonParameter;
            lvI.SubItems[4].Text = Constraint_Value;          
        }
        #endregion

        #region Node updating
        
        /// <summary>
        /// Go through all exceptions and update colors accordingly
        /// </summary>
        /// <param name="Exceptions"></param>
        public void UpdateNode(List<MM_OTS_Rule_Exception> Exceptions)
        {
            int CountThis = 0, CountMatched = 0;
            foreach (MM_OTS_Rule_Exception Exception in Exceptions)
                if (Exception.BaseRule == this)
                {
                    CountThis++;
                    if (Exception.NOMCR != null)
                        CountMatched++;
                }

            if (CountThis == 0)
                BaseNode.ForeColor = Color.Black;
            else if (CountThis == CountMatched)
                BaseNode.ForeColor = Color.Green;
            else
                BaseNode.ForeColor = Color.Red;
        }
        #endregion

        #region Processing
        /// <summary>
        /// Retrieve all elements into a List
        /// </summary>
        /// <param name="ElementCount"></param>
        public List<CIM_Element> RetrieveElements(ref int ElementCount)
        {
            List<CIM_Element> ParseElements = new List<CIM_Element>();
            if (this.RuleType == enumRuleType.Substation_Must_Have)
                ParseElements.AddRange(MM_Database_CIM.ElementsOfType("cim:Substation"));
            else
                foreach (KeyValuePair<String, int> Types in MM_Database_CIM.Types)
                    if (Types.Key.EndsWith(this.ElementType, StringComparison.CurrentCultureIgnoreCase))
                        ParseElements.AddRange(MM_Database_CIM.ElementsByType[(byte)Types.Value]);
            ElementCount += ParseElements.Count;
            ParseElements.TrimExcess();
            return ParseElements;
        }

        /// <summary>
        /// Process a rule
        /// </summary>
        /// <param name="CIMDb"></param>
        /// <param name="sConn"></param>
        /// <param name="Exceptions"></param>
        /// <param name="NOMCRs"></param>
        /// <param name="CurRule"></param>
        /// <param name="ParseElements"></param>
        /// <param name="tvExceptions"></param>
        public void ProcessRule(MM_Database_CIM CIMDb, SQLiteConnection sConn, List<MM_OTS_Rule_Exception> Exceptions, TreeView tvExceptions, DataTable NOMCRs, List<CIM_Element> ParseElements, ref int CurRule)
        {
                       
            if (this.RuleType == enumRuleType.A_MustHave_B)
            {
                int RequiredElements = 1;
                int.TryParse(Constraint_Value, out RequiredElements);
                foreach (CIM_Element Elem in ParseElements)
                {
                    if (Elem.ParseMultiples(ComparisonParameter).Length < RequiredElements)
                        Exceptions.Add(new MM_OTS_Rule_Exception(this, Elem,tvExceptions, NOMCRs));
                    CurRule++;
                }
            }
            else if (this.RuleType == enumRuleType.AttributeLimit)
            {
                int MaxLength = 1;
                int.TryParse(Constraint_Value, out MaxLength);
                foreach (CIM_Element Elem in ParseElements)
                {
                    if (Elem[ComparisonParameter].Length > MaxLength)
                        Exceptions.Add(new MM_OTS_Rule_Exception(this, Elem, tvExceptions, NOMCRs));
                    CurRule++;
                }
            }
            else if (this.RuleType == enumRuleType.RequireUnique)
            {
                String Res;
                UniqueNames = new Dictionary<string, CIM_Element>();
                foreach (CIM_Element Elem in ParseElements)
                {
                    if (UniqueNames.ContainsKey(Res = Elem[ComparisonParameter]))
                        Exceptions.Add(new MM_OTS_Rule_Exception(this, Elem, tvExceptions, NOMCRs));
                    else
                        UniqueNames.Add(Res, Elem);
                    CurRule++;
                }
            }
            else if (this.RuleType == enumRuleType.Substation_Must_Have)
            {
                foreach (CIM_Element Sub in ParseElements)
                {
                    bool FoundSource = false;
                    bool FoundTarget = false;
                    foreach (CIM_Element Elem in ParseElements)
                        if (Elem.Type.EndsWith(ElementType, StringComparison.CurrentCultureIgnoreCase))
                            FoundSource = true;
                        else if (Elem.Type.EndsWith(ComparisonParameter, StringComparison.CurrentCultureIgnoreCase))
                            FoundTarget = true;
                    if (FoundSource && !FoundTarget)
                        Exceptions.Add(new MM_OTS_Rule_Exception(this, Sub, tvExceptions, NOMCRs));
                    CurRule++;
                }
            }

            UpdateNode(Exceptions);
        }
        #endregion


        #region NOMCR testing and handling
        /// <summary>
        /// Test a NOMCR to determine whether it fits our criteria
        /// </summary>
        /// <param name="NOMCR"></param>
        /// <param name="Exception"></param>
        public bool TestNOMCR(DataRow NOMCR, MM_OTS_Rule_Exception Exception)
        {
            if (this.RuleType == enumRuleType.A_MustHave_B)
            { }
            else if (this.RuleType == enumRuleType.AttributeLimit && this.ElementType == Exception.BaseRule.ElementType)
            {
                int MaxLength = 1;
                int.TryParse(Constraint_Value, out MaxLength);
                String inLine;
                using (StringReader sRd = new StringReader((string)NOMCR["XML"]))
                while (!String.IsNullOrEmpty(inLine = sRd.ReadLine()))
                {
                    string[] splStr = inLine.Split('=');
                    if (splStr[0].EndsWith(ComparisonParameter, StringComparison.CurrentCultureIgnoreCase) && splStr[1].Length > MaxLength)
                        return false;
                }
            }
            else if (this.RuleType == enumRuleType.RequireUnique && this.ElementType == Exception.BaseRule.ElementType)
            {
                String inLine;
                using (StringReader sRd = new StringReader((string)NOMCR["XML"]))
                while (!String.IsNullOrEmpty(inLine = sRd.ReadLine()))
                {
                    string[] splStr = inLine.Split('=');
                    if (splStr[0].EndsWith(ComparisonParameter, StringComparison.CurrentCultureIgnoreCase) && UniqueNames.ContainsKey(splStr[1]))
                        return false;
                }
            }
            else if (this.RuleType == enumRuleType.Substation_Must_Have)
            {
            }
            return true;
        }

        /// <summary>
        /// Make the appropriate changes to handle the NOMCR's occurance.
        /// </summary>
        /// <param name="NOMCR"></param>
        public void ConfirmNOMCR(DataRow NOMCR)
        { }
        #endregion

        #region IComparable<MM_OTS_Rule> Members
        /// <summary>
        /// Compare two OTS rules
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo(MM_OTS_Rule other)
        {
            return Name.CompareTo(other.Name);
        }

        #endregion
    }
}
